/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-05
 * V4.0
 */
package com.jphenix.servlet.common;

import com.jphenix.driver.nodehandler.FNodeHandler;
import com.jphenix.kernel.baseobject.instanceb.ABase;
import com.jphenix.kernel.objectloader.exception.BeanException;
import com.jphenix.service.nodeloader.NodeService;
import com.jphenix.servlet.filter.PageFilter;
import com.jphenix.servlet.interfaceclass.ILayoutManager;
import com.jphenix.share.lang.SString;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.exceptions.MsgException;
import com.jphenix.standard.servlet.IActionContext;
import com.jphenix.standard.viewhandler.INodeHandler;
import com.jphenix.standard.viewhandler.IViewHandler;

import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.List;

/**
 * 页面布局处理类
 * com.jphenix.servlet.common.LayoutManager
 * 
 * 2021-03-15 去掉了废弃代码 ServiceManager
 * 
 * @author 刘虻
 * 2012-11-6 下午5:21:10
 */
@ClassInfo({"2021-03-15 17:49","页面布局处理类"})
public class LayoutManager extends ABase implements ILayoutManager {

  protected String 
             configFilePath = null   // 配置文件信息路径
            ,webBasePath    = null;  // 网站根路径
  
  protected HashMap<String,String>
      layoutFileMap         = null,  // 布局页面文件容器
      layoutCurrentKeyMap   = null;  // 当前文件在布局页面中的主键
  protected HashMap<String,HashMap<String,String>> 
      layoutOtherFileMap    = null;  // 布局页面中其它板块文件路径容器
  
  private   NodeService ns  = null;  // 页面对象管理类
  private   PageFilter  pf  = null;  // 页面（模板）请求过滤器
  /**
   * 构造函数
   * @author 刘虻
   */
  public LayoutManager() {
    super();
  }

  /**
   * 执行初始化
   * 刘虻
   * 2012-11-6 下午5:54:25
   * @throws Exception 异常
   */
  public void init() throws Exception {
    if(configFilePath==null) {
      throw new MsgException(this,"Layout Manager: Config File Path is Null");
    }
    //构建检测文件变化对象
    File configFile = new File(configFilePath);
    if(!configFile.exists()) {
      throw new MsgException(this,"Layout Manager: The File:["+configFilePath+"] Not Found");
    }
    INodeHandler configXml = FNodeHandler.newNodeHandler(); //构建新的xml处理类
    configXml.setXmlStyle(true);
    try {
      //Xml配置文件
      configXml.setStream(new FileInputStream(configFile),configFilePath); //设置xml文件
    }catch(Exception e) {
      e.printStackTrace();
      throw new BeanException("Layout Manager: Load File Exception Path:["+configFilePath+"]");
    }
    
    /*
      <?xml version="1.0" encoding='UTF-8'?>
      <root>
        <布局信息主键> <!-- 通用页面 -->
          <layoutfile>布局文件路径</layoutfile>
          <currentkey>当前文件在布局页面中的板块主键</currentkey>
          <keymap>布局页面中其它板块主键，多个用逗号分割（比如 head,foot）</keymap>
          <每个板块主键 比如 head>对应的文件路径</head>
          <foot>/layout/headfoot/nfoot.htm</foot>
        </布局信息主键>
      </root>
     */
    layoutFileMap = new HashMap<String,String>();
    layoutCurrentKeyMap = new HashMap<String,String>();
    layoutOtherFileMap = new HashMap<String,HashMap<String,String>>();
    
    //获取节点数组
    List<IViewHandler> subXmls = configXml.getChildDealNodes();
    IViewHandler cXml;                //子节点
    File cFile;                       //验证文件路径的文件
    String layoutFilePath;            //页面布局文件路径（相对路径）
    String currentKey;              //当前页面的主键
    List<String> keyMapList;      //布局页面中的其它主键字符序列
    for(IViewHandler subXml:subXmls) {
      String nodeName = subXml.nn(); //layout主键
      /*
       * 获取布局文件路径
       */
      cXml = subXml.getFirstChildNodeByNodeName("layoutfile");
      if(cXml.isEmpty()) {
        log.warning("LayoutManager: The Layout Block ["+nodeName+"] Not Found Layout File Path Info",this);
        continue;
      }
      layoutFilePath = "/"+cXml.nt();
      cFile = filesUtil.getFileByName(getWebBasePath()+layoutFilePath);
      if(!cFile.exists()) {
        log.warning("LayoutManager: The Layout File Not Exist ["+layoutFilePath+"] Path:["+cFile.getPath()+"]",this);
        continue;
      }
      /*
       * 获取当前页面在布局页面中的主键
       */
      cXml = subXml.getFirstChildNodeByNodeName("currentkey");
      if(cXml.isEmpty()) {
        log.warning("LayoutManager: The Layout Block ["+nodeName+"] Not Found currentkey Info",this);
        continue;
      }
      currentKey = cXml.nt();
      if(currentKey.length()<1) {
        log.warning("LayoutManager: The Layout Block ["+nodeName+"] The currentkey Info Is Null",this);
        continue;
      }
      
      layoutFileMap.put(nodeName,layoutFilePath);
      layoutCurrentKeyMap.put(nodeName,currentKey);
      
      /*
       * 布局页面中的其它主键字符串
       */
      HashMap<String,String> keyFileMap = new HashMap<String,String>(); //布局页面中其它板块文件路径
      layoutOtherFileMap.put(nodeName,keyFileMap);
      cXml = subXml.getFirstChildNodeByNodeName("keymap");
      if(cXml.isEmpty()) {
        continue;
      }
      //获取布局页面中其它板块主键序列
      keyMapList = BaseUtil.splitToList(cXml.nt(),",");
      String key; //板块主键元素
      for(int j=0;j<keyMapList.size();j++) {
        key = SString.valueOf(keyMapList.get(j));
        //获取对应的信息节点
        cXml = subXml.getFirstChildNodeByNodeName(key);
        if(cXml.isEmpty()) {
          continue;
        }
        cFile = filesUtil.getFileByName(getWebBasePath()+"/"+cXml.nt());
        if(!cFile.exists()) {
          log.warning("LayoutManager: The Layout File Not Exist ["
              +cXml.nt()+"] Path:["+cFile.getPath()+"] Block:["+nodeName+"] key:["+key+"]",this);
          continue;
        }
        keyFileMap.put(key,cFile.getPath());
      }
    }
    ns = bean(NodeService.class);
    pf = bean(PageFilter.class);
  }
  
  /**
   * 获取网站根路径
   * 刘虻
   * 2012-11-6 下午5:52:47
   * @return 网站根路径
   */
  public String getWebBasePath() {
    if(webBasePath==null) {
      //相对于 /WEB-INF/classes
      webBasePath = filesUtil.getAllFilePath("../../");
    }
    return webBasePath;
  }
  
  /**
   * 设置网站根路径
   * 刘虻
   * 2012-11-6 下午5:52:15
   * @param webBasePath 网站根路径
   */
  public void setWebBasePath(String webBasePath) {
    this.webBasePath = webBasePath;
  }
  
  /**
   * 获取配置文件路径
   * 刘虻
   * 2012-11-6 下午5:24:08
   * @return 配置文件路径
   */
  public String getConfigFilePath() {
    return  configFilePath;
  }
  
  /**
   * 设置配置文件路径
   * 刘虻
   * 2012-11-6 下午5:23:39
   * @param configFilePath 配置文件路径
   */
  public void setConfigFilePath(String configFilePath) {
    this.configFilePath = configFilePath;
  }

  /**
   * 覆盖方法
   * 刘虻
   * 2012-11-6 下午5:21:10
   */
  @Override
    public IViewHandler fixLayout(
      IViewHandler vh, IActionContext ac) throws Exception {
    if(vh==null) {
      return vh;
    }
    //页面布局主键
    String key = vh.getParameter("layout");
    if(key.length()<1) {
      return vh;
    }
    //页面布局文件路径
    String filePath = SString.valueOf(layoutFileMap.get(key));
    if(filePath.length()<1) {
      return vh;
    }
    //当前页面板块主键
    String currentKey = SString.valueOf(layoutCurrentKeyMap.get(key));
    if(currentKey.length()<1) {
      return vh;
    }
    ac.setAttribute(currentKey,vh); //放入上下文中
    
    /*
     * 获取布局模板中其它板块的主键与文件路径
     */
    //当前页面对应的其它板块信息容器
    HashMap<String,String> currentOtherMap = layoutOtherFileMap.get(key);
    if(currentOtherMap!=null){
      String keyEle;    //主键元素
      List<String> keyList = BaseUtil.getMapKeyList(currentOtherMap);
      for(int i=0;i<keyList.size();i++) {
        keyEle = SString.valueOf(keyList.get(i));
        //将页面文件路径放入板块对象
        ac.setAttribute(keyEle,SString.valueOf(currentOtherMap.get(keyEle)));
      }
    }
    //加载布局模板，不存在加载不到，都验证过的
    //除非哪只猴在程序启动后把模板给删了
    IViewHandler modelVh = ns.getNodeLoader().getNode(filePath,ac);
    //解析布局模板
    pf.parseNode(ac,modelVh);
    return modelVh;
  }
}
